//
// File:	Parser.cpp
// Author:	Thomas TISSOT-DUPONT
// Login:	<thomas.tissotdupont@free.fr>
//
// Create:	02/02/2010 at 09:13:41
// Purpose:	
//
#include "Parser.hpp"
#include "Trace.hpp"

const std::string Parser::stOpenBlock = "{";
const std::string Parser::stCloseBlock = "}";
const std::string Parser::stKeyEnd = ":";
const std::string Parser::stValueEnd = ";";
const std::string Parser::stTab = "\t";
const std::string Parser::stCR = "\r";
const std::string Parser::stNewLine = "\n";
const std::string Parser::stSpace = " ";
const std::string Parser::stBlank = Parser::stSpace + Parser::stTab + Parser::stCR + Parser::stNewLine;

//------------------------------------------------------------------
// Constructor / Destructor
//------------------------------------------------------------------
Parser::Parser()
	:	m_rootElement()
{
}

Parser::~Parser()
{
}

//------------------------------------------------------------------
// Parse from file
//------------------------------------------------------------------
bool Parser::ParseFromFile(const std::string &filePath)
{
	std::string fileBuffer;
	if (!File::GetContents(filePath, fileBuffer))
		return false;
	return ParseFromBuffer(fileBuffer);
}

//------------------------------------------------------------------
// Parse from buffer
//------------------------------------------------------------------
bool Parser::ParseFromBuffer(const std::string &fileBuffer)
{
	std::list<std::string> tokensList;
	Tokenizer tokenizer(stBlank, stOpenBlock + stCloseBlock + stKeyEnd + stValueEnd);
	tokenizer.Tokenize(fileBuffer, tokensList);
	return ParseFromTokensList(tokensList);
}

//------------------------------------------------------------------
// Parse from tokens list
//------------------------------------------------------------------
bool Parser::ParseFromTokensList(std::list<std::string> &tokensList)
{
	return ParseBlock(tokensList, &m_rootElement);
}

//------------------------------------------------------------------
// Root Element
//------------------------------------------------------------------
const Parser::Element &Parser::GetRootElement() const
{
	return m_rootElement;
}

//------------------------------------------------------------------
// Parse Block
//------------------------------------------------------------------
bool Parser::ParseBlock(std::list<std::string> &tokensList, Element *pBlock)
{
	if (tokensList.size() < 3)
		return false;

	//Begin Block
	std::string blockName = *tokensList.begin();
	std::string blockOpen = *(++tokensList.begin());
	if (blockOpen != Parser::stOpenBlock)
		return false;
	tokensList.pop_front();
	tokensList.pop_front();

	//Allocate Block
	Element *pNewBlock = new Element(blockName);
	pBlock->AddChild(pNewBlock);

	//Block Body
	while (ParseBlock(tokensList, pNewBlock) || ParserKeyValue(tokensList, pNewBlock))
	{
	}

	//End Block
	if (tokensList.empty() || tokensList.front() != Parser::stCloseBlock)
		return false;
	tokensList.pop_front();
	return true;
}

//------------------------------------------------------------------
// Parse KeyValue
//------------------------------------------------------------------
bool Parser::ParserKeyValue(std::list<std::string> &tokensList, Element *pBlock)
{
	if (tokensList.size() < 4)
		return false;
	std::string key = *tokensList.begin();
	std::string keyEnd = *(++tokensList.begin());
	std::string value = *(++(++tokensList.begin()));
	std::string valueEnd = *(++(++(++tokensList.begin())));
	if (keyEnd != Parser::stKeyEnd || valueEnd != Parser::stValueEnd)
		return false;
	pBlock->AddKeyValue(key, value);
	tokensList.pop_front();
	tokensList.pop_front();
	tokensList.pop_front();
	tokensList.pop_front();
	return true;
}

//------------------------------------------------------------------
// Clear
//------------------------------------------------------------------
void Parser::Clear()
{
	//HACK: it will destroy the previous instance of m_rootElement and free its children recursively
	m_rootElement = Parser::Element();
}
